﻿<template>
  <base-container>
    <!-- 搜索栏 -->
    <avue-table-search
      :option="searchOption"
      v-model="searchParams"
      @search-change="loadTableSearch"
      @search-reset="loadTableSearchReset"
    />

    <base-table-card>
      <template #header-right>
        <el-button type="primary" v-auth="[PERMS.SERVICE.CATEGORY.ADD]" @click="handleAddRoot">
          新增服务类目
        </el-button>
      </template>

      <avue-crud
        ref="crudRef"
        :option="tableOption"
        :data="tableData"
        v-model="formData"
        :page="page"
        :table-loading="tableLoading"
        @refresh-change="loadGetTableData"
        @current-change="loadTablePageChange"
        @size-change="loadTablePageSizeChange"
        @row-save="loadAdd"
        @row-update="loadEdit"
      >
        <!-- 上级类目（表单显示值） -->
        <template #parentName-form>
          <span>{{ formData?.parentName || "主类目" }}</span>
        </template>

        <!-- 展示状态开关 -->
        <template #displayStatus="{ row }">
          <el-switch
            :model-value="row.displayStatus"
            :loading="row._statusLoading"
            :active-value="1"
            :inactive-value="0"
            :disabled="!canToggleDisplay"
            :before-change="() => handleToggleDisplay(row)"
          />
        </template>

        <!-- 操作列 -->
        <template #menu="{ row }">
          <el-button type="primary" text v-auth="[PERMS.SERVICE.CATEGORY.UPDATE]" @click="handleEdit(row)">
            编辑
          </el-button>

          <el-button
            v-auth="[PERMS.SERVICE.CATEGORY.UPDATE]"
            v-if="canAddChild(row)"
            type="primary"
            text
            @click="handleAddChild(row)"
          >
            加下级
          </el-button>

          <template v-if="!canDelete(row)">
            <el-tooltip :content="getDeleteTip(row)" placement="top">
              <el-button v-auth="[PERMS.SERVICE.CATEGORY.DELETE]" text disabled>删除</el-button>
            </el-tooltip>
          </template>
          <el-button v-else v-auth="[PERMS.SERVICE.CATEGORY.DELETE]" type="danger" text @click="loadDel(row)">
            删除
          </el-button>
        </template>
      </avue-crud>
    </base-table-card>
  </base-container>
</template>

<script setup>
  /* ================== Imports ================== */

  import Option from "@/crudOption/service/serviceList";
  import { ElMessageBox } from "element-plus";
  import { useUserInfoStore } from "@/store/modules/userInfo.js";
  import { PERMS } from "@/constants/perms.js";

  /* ================== 权限 ================== */
  const user = useUserInfoStore();
  const canToggleDisplay = computed(() => user.hasAuth([PERMS.SERVICE.CATEGORY.ENABLE]));

  /* ================== CRUD Hook ================== */
  let {
    data: tableData,
    loading: tableLoading,
    page,
    search: searchParams,
    form: formData,
    get: loadGetTableData,
    add: loadAdd,
    edit: loadEdit,
    del: loadDel,
    pageNumChange: loadTablePageChange,
    pageSizeChange: loadTablePageSizeChange,
    searchReset: loadTableSearchReset,
    searchChange: loadTableSearch,
  } = useCrudHook({
    get: Api.service.GetServiceCategoryList,
    add: Api.service.AddServiceCategory,
    edit: Api.service.UpdateServiceCategory,
    del: Api.service.DelServiceCategory,
    dataRes: "data",

    // 搜索参数格式化
    getParamsFormat: p => {
      const params = { ...p };

      if (Array.isArray(params.createTime) && params.createTime.length === 2) {
        params.createTimeStart = params.createTime[0];
        params.createTimeEnd = params.createTime[1];
      }
      if (Array.isArray(params.updateTime) && params.updateTime.length === 2) {
        params.updateTimeStart = params.updateTime[0];
        params.updateTimeEnd = params.updateTime[1];
      }
      if (params.displayStatus === "") delete params.displayStatus;

      delete params.createTime;
      delete params.updateTime;
      return params;
    },

    // 新增/编辑参数格式化
    addParamsFormat: row => ({
      ...row,
      applicableObjects: Array.isArray(row.applicableObjects)
        ? row.applicableObjects.join(",")
        : row.applicableObjects || "",
      displayStatus: Number(row.displayStatus ?? 1),
      sortNo: Number(row.sortNo ?? 1),
      platform: 1,
    }),
    editParamsFormat: row => ({
      ...row,
      applicableObjects: Array.isArray(row.applicableObjects)
        ? row.applicableObjects.join(",")
        : row.applicableObjects || "",
      displayStatus: Number(row.displayStatus ?? 1),
      sortNo: Number(row.sortNo ?? 1),
    }),
  });

  /* ================== 组件配置 ================== */
  const { searchOption, tableOption } = Option();

  /* ================== 引用 ================== */
  const crudRef = ref(null);

  /* ================== 适用对象字典：全量 & 裁剪 ================== */
  // 1) 获取「适用对象」字段配置
  const getApplicableCol = () => tableOption.value?.group?.[0]?.column?.applicableObjects;
  // 2) 全量字典缓存
  const ALL_APPLICABLE = ref([]);
  // 3) 拉取字典，并写入配置（根级默认显示全部）
  async function loadApplicableDict() {
    try {
      const res = await Api.dict.GetListChildrenByDictCode({
        dictCode: "qingyun.support.serviceCenter.applicableObject",
      });
      ALL_APPLICABLE.value = (res?.data || []).map(i => ({
        label: i.dictLabel,
        value: String(i.dictValue),
      }));
    } finally {
      const col = getApplicableCol();
      if (col) col.dicData = ALL_APPLICABLE.value.slice();
    }
  }
  // 工具：解析逗号串为去空格数组
  const parseCodes = v =>
    String(v ?? "")
      .split(",")
      .map(s => s.trim())
      .filter(Boolean);

  // 工具：按 codes 裁剪可选项（codes=[] 表示全部）
  function limitApplicableTo(codes) {
    const col = getApplicableCol();
    if (!col) return;
    const set = new Set((codes || []).map(String));
    col.dicData =
      set.size === 0 ? ALL_APPLICABLE.value.slice() : ALL_APPLICABLE.value.filter(o => set.has(String(o.value)));
  }

  /* ================== 新增 / 编辑 ================== */
  // 递归找父级名称
  function getParentName(list, pid) {
    if (!pid) return "";
    for (const n of list || []) {
      if (n.id === pid) return n.categoryName;
      const hit = getParentName(n.children, pid);
      if (hit) return hit;
    }
    return "";
  }
  // 递归找父节点
  function findNodeById(list, id) {
    if (!id) return null;
    for (const n of list || []) {
      if (n.id === id) return n;
      const hit = findNodeById(n.children, id);
      if (hit) return hit;
    }
    return null;
  }

  // 根级新增：显示全部选项
  function handleAddRoot() {
    formData.value.parentId = 0;
    formData.value.parentName = "主类目";
    limitApplicableTo([]); // 全部
    formData.value.applicableObjects = [];
    crudRef.value?.rowAdd();
  }

  // 加下级：选项=父级 codes，并默认勾选父级
  function handleAddChild(row) {
    const parentCodes = parseCodes(row.applicableObjects);
    limitApplicableTo(parentCodes);
    formData.value.parentId = row.id;
    formData.value.parentName = row.categoryName;
    formData.value.applicableObjects = parentCodes.slice();
    crudRef.value?.rowAdd();
  }

  // 编辑：同样受父级限制，越界值自动收敛
  function handleEdit(row) {
    const parent = findNodeById(tableData.value, row.parentId);
    const parentCodes = parseCodes(parent?.applicableObjects);
    const ownCodes = parseCodes(row.applicableObjects);

    limitApplicableTo(parentCodes);
    const allow = new Set(parentCodes);
    const safeOwn = parentCodes.length ? ownCodes.filter(c => allow.has(c)) : ownCodes;

    const parentName = getParentName(tableData.value, row.parentId) || "主类目";
    crudRef.value?.rowEdit({
      ...row,
      parentName,
      applicableObjects: safeOwn,
    });
  }

  /* ================== 层级标注 & 操作列规则 ================== */
  const levelMap = ref(new Map());
  function markNodeLevels(list, level = 1) {
    if (!Array.isArray(list)) return;
    for (const node of list) {
      levelMap.value.set(node.id, level);
      node._level = level; // 仅调试
      if (Array.isArray(node.children) && node.children.length) {
        markNodeLevels(node.children, level + 1);
      }
    }
  }
  watch(
    tableData,
    val => {
      levelMap.value = new Map();
      markNodeLevels(Array.isArray(val) ? val : [], 1);
    },
    { immediate: true }
  );

  const hasChildren = row => Array.isArray(row?.children) && row.children.length > 0;
  const hasContent = row => Number(row?.contentNum ?? 0) > 0;
  const canAddChild = row => (levelMap.value.get(row.id) ?? 1) < 3 && row.displayStatus == 1;
  const canDelete = row => !hasChildren(row) && !hasContent(row);
  const getDeleteTip = row => {
    if (hasChildren(row)) return "该类目存在下级，请先删除其下级类目后再删除";
    if (hasContent(row)) return "该类目下已有内容，请先迁移内容后再删除";
    return "";
  };

  /* ================== 展示状态开关（开启/关闭 & 子级提示） ================== */
  async function hasChildrenAsync(row) {
    if (Array.isArray(row.children)) return row.children.length > 0;
    if (typeof row.childCount === "number") return row.childCount > 0;
    try {
      const r = await Api.service.GetCategoryChildrenCount?.({ id: row.id });
      const n = r?.data?.count ?? r?.count ?? 0;
      return n > 0;
    } catch {
      return false;
    }
  }
  async function handleToggleDisplay(row) {
    const closing = row.displayStatus === 1;
    const nextVal = closing ? 0 : 1;

    let tip = closing ? "确定要关闭该类目吗？关闭后不在前端展示。" : "确定要开启该类目吗？";
    if (closing && (await hasChildrenAsync(row))) {
      tip = "关闭后其下的子级也将同步关闭，确定关闭吗？";
    }

    try {
      await ElMessageBox.confirm(tip, "提示", { type: "warning" });
    } catch {
      return false;
    }

    row._statusLoading = true;
    try {
      await Api.service.StatusCategoryEnableChange({ id: row.id, displayStatus: nextVal });
      $msg.success(nextVal ? "已开启" : "已关闭");
      await loadGetTableData();
      return true;
    } catch (e) {
      console.error("[onToggleCategory]", e);
      return false;
    } finally {
      row._statusLoading = false;
    }
  }

  /* ================== 菜单权限（可选） ================== */
  tableOption.value.menu = computed(() =>
    user.hasAuthAll([PERMS.SERVICE.CATEGORY.UPDATE, PERMS.SERVICE.CATEGORY.DELETE])
  );

  /* ================== 挂载 ================== */
  onMounted(async () => {
    await loadApplicableDict(); // ★ 先把字典写进配置
    loadGetTableData();
  });
</script>

<style lang="scss" scoped></style>
